#!/usr/bin/python

###########################################################################
# bughauz 0.1: a free, multiplayer bughouse program, still in development #
# Copyright (C) 2009 Martijn van Schaardenburg                            #
#                                                                         #
# This file is part of bughauz.                                           #
#                                                                         #
# bughauz is free software: you can redistribute it and/or modify         #
# it under the terms of the GNU General Public License as published by    #
# the Free Software Foundation, either version 3 of the License, or       #
# (at your option) any later version.                                     #
#                                                                         #
# This program is distributed in the hope that it will be useful,         #
# but WITHOUT ANY WARRANTY; without even the implied warranty of          #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           #
# GNU General Public License for more details.                            #
#                                                                         #
# You should have received a copy of the GNU General Public License       #
# along with this program.  If not, see <http://www.gnu.org/licenses/>.   #
#                                                                         #
# contact: mvanschaarde@hmc.edu or                                        #
# Martijn van Schaardenburg                                               #
# c/o Harvey Mudd College                                                 #
# 340 E. Foothill Blvd,                                                   #
# Claremont, Ca 91711                                                     #
#                                                                         #
# The images included as part of this release are covered under the LGPL  #
# and are Copyright (C) Maurizio Monge.  For more information, contact    #
# maurizio.monge@gmail.com.                                               #
###########################################################################

from bughauz import *

def serverMain():
    print '###################################################################'
    print '# bughauz Copyright (C) 2009 Martijn van Schaardenburg            #'
    print '# This program comes with ABSOLUTELY NO WARRANTY.  It is licensed #'
    print '# under the GPL v.3. For more information see                     #'
    print '# <http://www.gnu.org/copyleft/gpl.html>.                         #'
    print '###################################################################'

    games, player_connections, sockets = setup()
    
    play(games, player_connections, sockets)
    
    print 'Closing connections ... ',
    for p in sockets:
        p.close()
    print 'done'

def setup():
    num_games = int(sys.argv[1])
    
    serversocket = socket.socket()
    serversocket.bind(('', BUGHOUSE_PORTNUM))
    
    # magic number. most people recommend 5
    serversocket.listen(20)
    
    # magic number.  Also unused, and wrongly formatted
    time_control = (1000, 2)
    
    print 'Awaiting player connections ...'
    
    messages = {}
    sockets = []
    
    while len(sockets) < 2 * num_games:
        sock = serversocket.accept()[0]
        sockets.append(sock)
        messages[sock] = [None, '', []]
    
    print 'All players have joined.  Receiving Player info...'
    
    # it would be nice if you could receive some of the pinf messages and print
    # 'gertrude connected' before all players have connected to the server.
    
    num_info = 0
    while num_info < num_games * 2:
        toRead, a, b = select.select(sockets, [], [], 0)
        
        for s in toRead:
            messages[s][1] += s.recv(MESSAGE_SIZE)
            messages[s] = processMbufs(messages[s])
            
            for i in range(len(messages[s][2])):
                if messages[s][2][i].startswith('pinf'):
                    
                    new_player = extractPlayer(messages[s][2].pop(i))
                    new_player.timer = time_control
                    new_player.ID = num_info
                    messages[s][0] = new_player
                    
                    num_info += 1
                    print new_player.name + ' connected.'

        time.sleep(0.1)
        
    players = map(lambda key: messages[key][0], messages)
    
    ready = False
    while not ready:
        # TODO range(5) is just an arbitrary placeholder here.
        timer = range(5)
        games = randomMatches(players, timer)

        for g in games:
            print 'game: ', g
    
        com = raw_input('ready to proceed? (y/n) ').strip()
        if com.lower() == 'y':
            ready = True
    
    print 'Sending game list to all players ... ',
    updateAllGameList(messages, games)
    print 'done'
    
    print 'Sending whoami to all players ... ',
    updateAllWhoAmI(messages, games)
    print 'done'
                                           
    return games, messages, sockets
    
def extractPlayer(s):
    return pickle.loads(removeTag(s))
    
def play(games, messages, sockets):
    while True:
        toRead, a, b = select.select(sockets, [], [])
        
        for s in toRead:
            messages[s][1] += s.recv(MESSAGE_SIZE)
            pobj, mbuf, msg_list = processMbufs(messages[s])
            
            while len(msg_list) > 0:
                process(msg_list.pop(0), pobj, games[pobj.game_ind], games, messages)
                
            messages[s] = [pobj, mbuf, msg_list]
        
        time.sleep(0.1)
    return
    
def process(msg, p, g, games, messages):
    global start_time
    
    if msg.startswith('move') or msg.startswith('drop'):
        m = removeTag(msg)
        
        (legal, board_after_move, captured) = g.tryMove(p, m)
        
        if legal:
            move_time = time.time()
            
            if g.started == False:
                start_time = move_time
                for gam in games:
                    gam.startTimer(start_time)
            
            g.updateBoardState(board_after_move)
            if captured != None:
                recipient = games[(p.game_ind + 1) % len(games)]
                
                if captured.prom:
                    captured.typ = 'p'
                    captured.prom = False
                
                recipient.drops[recipient.players[captured.color]][captured] += 1
            
            updateAllGameList(messages, games)
        else:
            print 'illegal move from player', p
            
    if msg.startswith('undo'): # msg == 'undo'
        if p not in g.undo_requests:
            g.undo_requests.append(p)
        
        # could also do len(game.undo_requests == 2)
        if p in g.undo_requests and g.opponents[p] in g.undo_requests:
            print 'undo being executed with players', p, g.opponents[p]
            g.undo()
            updateAllGameList(messages, games)
    
def randomMatches(players, timer):
    team1 = random.sample(players, len(players)/2)
    team2 = [p for p in players if p not in team1]
    
    games = []
    
    for i in range(len(team1)):
        p1 = team1[i]
        p2 = team2[i]
        
        p1.team = 0
        p2.team = 1

        p1.game_ind = i
        p2.game_ind = i
        
        # i % 2 gives white, black, white, ...
        if i % 2 == WHITE:
            p1.color = WHITE
            p2.color = BLACK
            games.append(Game(p1, p2, timer))
        else:
            p1.color = BLACK
            p2.color = WHITE
            games.append(Game(p2, p1, timer))
    
    return games

def updateAllWhoAmI(messages, games):
    for sock in messages:
        p, mbuf, msg_list = messages[sock]

        found = False        
        for i in range(len(games)):
            if p in games[i].players:
                sendToClient(sock, 'whoami ' + str(i) + ' ' + str(games[i].players.index(p)))
                found = True
            
        if not found:
            raise ValueError, ('Player not in list, fix __cmp__' + repr(p))
        
def updateAllGameList(messages, games):
    msg = 'ug ' + pickle.dumps(games)
    
    for sock in messages:
        sendToClient(sock, msg)
 
def sendToClient(c_sock, msg):
    c_sock.sendall(PASSCODE + ' ' + str(len(msg)) + ' ' + msg)
    
if __name__ == '__main__':
    serverMain()
